import tkinter as tk
from tkinter import scrolledtext
import os
import subprocess
import sys
import threading
import queue
import re
import time

class TerminalTab:
    def __init__(self, parent, initial_path):
        self.parent = parent
        self.current_path = initial_path
        self.command_history = []
        self.history_index = -1
        self.current_command = ""
        self.running_process = None
        self.output_queue = queue.Queue()
        self.prompt_length = 0
        
        # 创建主框架
        self.frame = tk.Frame(parent)
        self.frame.pack(fill=tk.BOTH, expand=True)
        
        # 创建终端文本区域（可编辑）
        self.terminal_text = scrolledtext.ScrolledText(self.frame, wrap=tk.WORD, font=('SimHei', 10))
        self.terminal_text.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
        self.terminal_text.focus_set()
        
        # 绑定事件
        self.terminal_text.bind("<Return>", self.execute_command)
        self.terminal_text.bind("<Up>", self.navigate_history)
        self.terminal_text.bind("<Down>", self.navigate_history)
        self.terminal_text.bind("<KeyPress>", self.handle_keypress)
        self.terminal_text.bind("<Control-c>", self.handle_ctrl_c)
        
        # 显示欢迎信息
        self.terminal_text.insert(tk.END, "欢迎使用智能终端\n")
        self.terminal_text.insert(tk.END, f"当前目录: {self.current_path}\n")
        self.terminal_text.insert(tk.END, "输入命令并按Enter执行，使用上下键浏览历史命令\n\n")
        
        # 显示提示符
        self.show_prompt()
        
        # 启动输出处理线程
        self.running = True
        self.output_thread = threading.Thread(target=self.process_output)
        self.output_thread.daemon = True
        self.output_thread.start()
    
    def show_prompt(self):
        # 显示当前路径提示符
        prompt = f"{self.current_path}>"
        self.terminal_text.insert(tk.END, prompt)
        self.prompt_length = len(prompt)
        self.terminal_text.mark_set("insert", tk.END)
        self.terminal_text.focus_set()
    
    def insert_text(self, text):
        # 向终端插入文本但不执行
        self.terminal_text.insert(tk.END, text)
        self.terminal_text.mark_set(tk.INSERT, tk.END)
        self.terminal_text.focus_set()
        
    def handle_keypress(self, event):
        # 限制用户只能在命令输入区域编辑
        current_pos = self.terminal_text.index(tk.INSERT)
        line, column = map(int, current_pos.split('.'))
        
        # 获取最后一行的索引
        last_line = self.terminal_text.index(f"end-1l").split('.')[0]
        
        # 检查是否在最后一行且在提示符位置之前
        if line == int(last_line) and column < self.prompt_length:
            # 移动光标到提示符后面
            self.terminal_text.mark_set(tk.INSERT, f"{last_line}.{self.prompt_length}")
            return "break"
        
        # 检查是否尝试删除提示符
        if event.keysym in ('BackSpace', 'Delete'):
            # 获取选择范围（如果有）
            try:
                sel_start = self.terminal_text.index(tk.SEL_FIRST)
                sel_end = self.terminal_text.index(tk.SEL_LAST)
                sel_start_line, sel_start_col = map(int, sel_start.split('.'))
                sel_end_line, sel_end_col = map(int, sel_end.split('.'))
                
                # 检查选择范围是否包含提示符
                contains_prompt = False
                contains_prompt |= (sel_start_line == int(last_line) and sel_start_col < self.prompt_length)
                contains_prompt |= (sel_end_line == int(last_line) and sel_end_col <= self.prompt_length)
                if contains_prompt:
                    # 阻止删除提示符
                    return "break"
            except tk.TclError:
                # 没有选择文本，检查单个删除操作
                if line == int(last_line) and column <= self.prompt_length:
                    # 阻止删除提示符
                    return "break"
    
    def execute_command(self, event=None):
        # 获取最后一行（命令行）的内容
        last_line = self.terminal_text.index(f"end-1l").split('.')[0]
        command_line = self.terminal_text.get(f"{last_line}.{self.prompt_length}", f"{last_line}.end")
        command = command_line.strip()
        
        # 换行
        self.terminal_text.insert(tk.END, "\n")
        
        if not command:
            # 显示新的提示符
            self.show_prompt()
            return "break"
        
        # 保存到历史记录
        self.command_history.append(command)
        self.history_index = len(self.command_history)
        
        # 处理特殊命令
        if command.lower() == 'exit':
            self.terminal_text.insert(tk.END, "退出终端\n")
            # 这里可以添加关闭标签页的逻辑
        elif command.lower().startswith('cd '):
            self.handle_cd(command[3:].strip())
        elif command.lower() == 'cls':
            # 清空文本区域并重新显示欢迎信息和提示符
            self.terminal_text.delete(1.0, tk.END)
            self.terminal_text.insert(tk.END, "欢迎使用智能终端\n")
            self.terminal_text.insert(tk.END, f"当前目录: {self.current_path}\n")
            self.terminal_text.insert(tk.END, "输入命令并按Enter执行，使用上下键浏览历史命令\n\n")
            self.show_prompt()
        else:
            # 执行其他命令
            self.run_command(command)
        
        return "break"
    
    def handle_cd(self, path):
        # 处理cd命令
        print(f"terminal_tab: 收到目录切换请求，目标路径: {path}")
        # 在终端中显示执行的cd命令
        self.terminal_text.insert(tk.END, f"cd {path}\n")
        
        try:
            # 处理相对路径和绝对路径
            print(f"terminal_tab: 当前路径: {self.current_path}")
            if path.startswith('~'):
                path = os.path.expanduser(path)
                print(f"terminal_tab: 扩展用户路径: {path}")
            elif not os.path.isabs(path):
                path = os.path.join(self.current_path, path)
                print(f"terminal_tab: 构建绝对路径: {path}")
            
            # 切换目录
            print(f"terminal_tab: 尝试切换到: {path}")
            os.chdir(path)
            self.current_path = os.getcwd()
            print(f"terminal_tab: 成功切换到: {self.current_path}")
            
        except Exception as e:
            error_msg = f"错误: {str(e)}"
            print(f"terminal_tab: {error_msg}")
            self.terminal_text.insert(tk.END, f"{error_msg}\n")
        
        # 显示新的提示符
        print(f"terminal_tab: 显示新提示符")
        self.show_prompt()
    
    def run_command(self, command):
        # 在新线程中执行命令
        threading.Thread(target=self._execute_command, args=(command,)).start()
    
    def _execute_command(self, command):
        try:
            # 构建进程
            if sys.platform == 'win32':
                # 使用cmd.exe执行命令，通常比PowerShell更快
                process_args = ["cmd.exe", "/c", command]
                self.running_process = subprocess.Popen(
                    process_args,
                    stdout=subprocess.PIPE,
                    stderr=subprocess.PIPE,
                    stdin=subprocess.PIPE,
                    text=True,
                    cwd=self.current_path,
                    bufsize=1  # 行缓冲模式，提高实时输出性能
                )
            else:
                self.running_process = subprocess.Popen(
                    ["/bin/bash", "-c", command],
                    stdout=subprocess.PIPE,
                    stderr=subprocess.PIPE,
                    stdin=subprocess.PIPE,
                    shell=False,
                    text=True,
                    cwd=self.current_path,
                    bufsize=1  # 行缓冲模式
                )
            
            # 读取输出 - 优化版本，确保完整读取所有输出
            stdout_output = []
            stderr_output = []
            
            # 使用线程分别读取stdout和stderr
            def read_stdout():
                for line in iter(self.running_process.stdout.readline, ''):
                    self.output_queue.put(line)
                    stdout_output.append(line)
                    
            def read_stderr():
                for line in iter(self.running_process.stderr.readline, ''):
                    self.output_queue.put(line)
                    stderr_output.append(line)
            
            # 启动读取线程
            stdout_thread = threading.Thread(target=read_stdout)
            stderr_thread = threading.Thread(target=read_stderr)
            stdout_thread.daemon = True
            stderr_thread.daemon = True
            stdout_thread.start()
            stderr_thread.start()
            
            # 等待进程结束
            self.running_process.wait()
            
            # 等待读取线程完成
            stdout_thread.join(timeout=0.5)
            stderr_thread.join(timeout=0.5)
            
            # 输出结束标志
            self.output_queue.put(None)
            
        except Exception as e:
            self.output_queue.put(f"执行命令时出错: {str(e)}\n")
            self.output_queue.put(None)
    
    def process_output(self):
        # 处理输出队列中的内容 - 优化版本
        output_buffer = []
        buffer_flush_interval = 0.05  # 缓冲区刷新间隔(秒)
        last_flush_time = time.time()
        
        while self.running:
            try:
                # 从队列中获取输出
                output = self.output_queue.get(timeout=0.1)
                
                if output is None:
                    # 如果缓冲区有内容，先刷新
                    if output_buffer:
                        self._flush_output_buffer(output_buffer)
                        output_buffer = []
                    
                    # 命令执行完毕，显示新的提示符
                    self.terminal_text.after(0, self.show_prompt)
                else:
                    # 将输出添加到缓冲区
                    output_buffer.append(output)
                    
                    # 检查是否需要刷新缓冲区
                    current_time = time.time()
                    if current_time - last_flush_time > buffer_flush_interval or len(output_buffer) > 50:
                        self._flush_output_buffer(output_buffer)
                        output_buffer = []
                        last_flush_time = current_time
                
                # 标记任务完成
                self.output_queue.task_done()
            except queue.Empty:
                # 队列为空，如果缓冲区有内容且超过刷新间隔，刷新缓冲区
                if output_buffer:
                    current_time = time.time()
                    if current_time - last_flush_time > buffer_flush_interval:
                        self._flush_output_buffer(output_buffer)
                        output_buffer = []
                        last_flush_time = current_time
                pass
            except Exception as e:
                # 处理错误
                print(f"处理输出时出错: {str(e)}")
    
    def _flush_output_buffer(self, buffer):
        # 批量刷新输出缓冲区，减少UI更新次数
        if buffer:
            output_text = ''.join(buffer)
            self.terminal_text.after(0, lambda out=output_text: [self.terminal_text.insert(tk.END, out), self.terminal_text.see(tk.END)])
    
    def navigate_history(self, event):
        # 导航历史命令
        if not self.command_history:
            return 'break'
        
        # 获取当前命令行位置
        last_line = self.terminal_text.index(f"end-1l").split('.')[0]
        
        # 处理上箭头
        if event.keysym == 'Up':
            if self.history_index > 0:
                # 保存当前正在编辑的命令
                if self.history_index == len(self.command_history):
                    current_command = self.terminal_text.get(f"{last_line}.{self.prompt_length}", f"{last_line}.end")
                    if current_command.strip():
                        # 如果当前有未执行的命令，保存到临时变量
                        self.current_command = current_command
                
                self.history_index -= 1
                # 更新命令行
                self.terminal_text.delete(f"{last_line}.{self.prompt_length}", f"{last_line}.end")
                self.terminal_text.insert(f"{last_line}.{self.prompt_length}", self.command_history[self.history_index])
        # 处理下箭头
        elif event.keysym == 'Down':
            if self.history_index < len(self.command_history) - 1:
                self.history_index += 1
                # 更新命令行
                self.terminal_text.delete(f"{last_line}.{self.prompt_length}", f"{last_line}.end")
                self.terminal_text.insert(f"{last_line}.{self.prompt_length}", self.command_history[self.history_index])
            else:
                if self.history_index == len(self.command_history) - 1:
                    # 到达历史记录底部，显示之前保存的当前命令或清空
                    self.history_index = len(self.command_history)
                    self.terminal_text.delete(f"{last_line}.{self.prompt_length}", f"{last_line}.end")
                    if self.current_command:
                        self.terminal_text.insert(f"{last_line}.{self.prompt_length}", self.current_command)
        
        # 移动光标到行末
        self.terminal_text.mark_set(tk.INSERT, tk.END)
        return 'break'
    
    def update_command_entry(self):
        # 更新命令输入框显示历史命令
        if 0 <= self.history_index < len(self.command_history):
            self.command_entry.delete(0, tk.END)
            self.command_entry.insert(0, self.command_history[self.history_index])
    
    def handle_ctrl_c(self, event):
        # 处理Ctrl+C快捷键，终止正在执行的命令
        if self.running_process and self.running_process.poll() is None:
            # 向终端显示中断信息
            self.terminal_text.after(0, lambda: self.terminal_text.insert(tk.END, "\n^C\n"))
            
            # 终止进程
            try:
                # 获取进程ID用于日志和额外终止尝试
                pid = self.running_process.pid
                
                # 对于Windows，使用terminate()并确保完全终止
                if sys.platform == 'win32':
                    # 首先尝试优雅终止
                    self.running_process.terminate()
                    
                    # 等待进程终止
                    try:
                        self.running_process.wait(timeout=0.5)
                    except subprocess.TimeoutExpired:
                        # 如果超时，使用taskkill命令强制终止整个进程树
                        self.output_queue.put(f"进程未响应终止信号，尝试强制终止进程树...\n")
                        try:
                            subprocess.run(
                                ["taskkill", "/F", "/T", "/PID", str(pid)],
                                stdout=subprocess.PIPE,
                                stderr=subprocess.PIPE,
                                text=True,
                                timeout=1
                            )
                        except Exception as kill_error:
                            self.output_queue.put(f"强制终止失败: {str(kill_error)}\n")
                else:
                    # 对于Unix系统，发送SIGINT信号（类似于Ctrl+C）
                    import signal
                    try:
                        os.kill(self.running_process.pid, signal.SIGINT)
                        self.running_process.wait(timeout=0.5)
                    except subprocess.TimeoutExpired:
                        # 如果SIGINT失败，尝试更强制的SIGTERM
                        os.kill(self.running_process.pid, signal.SIGTERM)
                        self.running_process.wait(timeout=0.5)
                
                # 确保running_process被重置为None
                self.running_process = None
                
                # 清空输出队列并显示新提示符
                while not self.output_queue.empty():
                    try:
                        self.output_queue.get_nowait()
                        self.output_queue.task_done()
                    except queue.Empty:
                        break
                
                # 标记命令结束
                self.output_queue.put(None)
            except Exception as e:
                self.output_queue.put(f"无法中断命令: {str(e)}\n")
                # 确保状态一致
                self.running_process = None
                self.output_queue.put(None)
        return "break"
    
    def stop(self):
        # 停止终端
        self.running = False
        if self.running_process:
            try:
                self.running_process.terminate()
                self.running_process.wait(timeout=1)
            except:
                pass